#ifndef __KEW_BASE_H__
#define __KEW_BASE_H__

#include <gc.h>
#include <glib.h>

G_BEGIN_DECLS

/* Forward-declare struct types */

typedef struct _KewSandboxObject KewSandboxObject;
typedef struct _KewThreadContext KewThreadContext;
typedef struct _KewCallFrame KewCallFrame;
typedef struct _KewEnsuredStackFrame KewEnsuredStackFrame;
typedef void (*KewEnsuredStackFrameCallback)(gpointer data);
typedef unsigned KewSelector;
typedef unsigned KewCallShape;
typedef struct _KewDispatchMap KewDispatchMap;
typedef struct _KewType KewType;
typedef struct _KewObject KewObject;
typedef struct _KewUserObject KewUserObject;
typedef void (*KewMethod)(KewObject *receiver, KewObject **args, KewObject **results);
typedef char *KewSourcePoint;

/* PRIMITIVE MEMORY ALLOCATION
 */

#define kew_alloc(size) (GC_malloc(size))
#define kew_new_array(type, number) ((type *) kew_alloc(sizeof(type) * (number)))
#define kew_new(type) kew_new_array((type), 1)
#define kew_new_padded(basetype, paddingtype, number) ((basetype *) kew_alloc(sizeof(basetype) + sizeof(paddingtype) * (number)))

/* INTERNAL KEW STATE
 * These should be treated as private and not accessed directly
 */

extern int kew__initialized = 0;
extern char *kew__version;
extern GHashTable *kew__selectors;
extern GHashTable *kew__call_shapes;
/* FIXME: add GHashTable *kew__components_by_fingerprint; */


/* THREAD SYSTEM SUPPORT
 * Currently multiple threads are not implemented, so there's nothing for you to do.
 * NOTE: many platform's thread systems are not supported by libgc, so having Kew
 * support threads at all is unlikely to be particularly portable.
 */

struct _KewThreadSystem {
    KewThreadContext *(*current_context)(void);
    void (*lock_selectors)(void);
    void (*unlock_selectors)(void);
    void (*lock_call_shapes)(void);
    void (*unlock_call_shapes)(void);
};

extern KewThreadSystem *kew__thread_system;
extern KewThreadContext kew__single_thread_context;

extern KewThreadSystem kew_thread_system_single;


/* INITIALIZATION
 * Each thread that will use Kew must call kew_init at least once. Calling more
 * than once is safe and efficient.
 */

void kew_init(void);
char *kew_version(void);


/* STACK HANDLING
 * Each thread (currently you're only allowed one!!) is associated with thread-
 * specific context information required by Kew.  This is always information that
 * needs to be stored on the stack.  For example, stack traces are computed by
 * walking back down special stack frames set up by the message sending function.
 *
 * Also of great importance are "ensured" frames, which provide a basis for
 * correct handling stack unwinding when calling continuations.  Each frame allows
 * something to be done as the stack unwinds (and also unwinds all the Kew call frames).
 * Note that continuations themselves are not implemented in the VM, but in the base
 * library.
 */
 
struct _KewThreadContext {
    KewEnsuredStackFrame *top_ensured_stack_frame;
    KewCallFrame *top_call_frame;
    GHashTable *extensions;
};

struct _KewEnsuredStackFrame {
    KewEnsuredStackFrame *next;
    KewEnsuredStackFrameCallback *callback;
    gpointer data;
};

struct _KewCallFrame {
    KewSandboxObject *sandbox;
    KewSourcePoint source_point;
    KewObject *receiver;
    KewObject **args;
    KewCallFrame *next;
};

#define KEW_ENSURED_FRAME_BEGIN(ENV, ENSURED_CALLBACK, ENSURED_CALLBACK_DATA) \
do { \
    KewEnsuredStackFrame __kew_new_frame; \
    __kew_new_frame.callback = (ENSURED_CALLBACK); \
    __kew_new_frame.data = (ENSURED_CALLBACK_DATA); \
    __kew_new_frame.next = (ENV)->top_ensured_stack_frame; \
    (ENV)->top_ensured_stack_frame = &__kew_new_frame;

#define KEW_ENSURED_FRAME_END(ENV) \
    (ENV)->top_ensured_stack_frame = __kew_new_frame.next; \
    (*(ENSURED_CALLBACK))((ENV), (ENSURED_CALLBACK_DATA)); \
} while (0);


void kew_stack_unwind(KewDynamicEnvironment *env, gpointer target_address);

/* Caller frees result */
GString *kew_stack_trace(KewDynamicEnvironment *env);

/* VIRTUAL MACHINES AND INITIALIZATION
 * Entering the VM requires the thread to become "attached".  This allows some thread-specific
 * data to be properly initialized.  This remains in effect until the thread returns from the
 * attachment function (if ever).  From that point on, the thread has access to a Kew
 * environment that basically stores thread-specific info needed by Kew.
 *
 * This attaches the current thread to the VM and runs a user-supplied callback.
 *
 * FIXME: Support for using more than 1 thread simultaneously with the VM almost certainly
 * doesn't work.  If it does, it's by accident.  Don't be tempted to use it yet.
 */

gpointer kew_stack_enter(KewThreadMain main_function, gpointer main_function_data);


/* COMPONENTS AND SECURITY CONTEXTS
 * Each component exists in its own security context, which provides the information for
 * security isolation rules to work.  Each component also has a reference to its
 * container-provided resources (which might be shared with other components).
 *
 * See security.c for a description of how security works.
 */

struct _KewSandboxObject {
    KewObject super;
    
    KewSandboxObject *parent;
    KewObject *block;
};


/* Check security: returns true if access should be allowed to dest from source. */
gboolean kew_sandbox_check(KewSandboxObject *source, KewSandboxObject *dest);


/* METHOD SELECTORS
 * Each method and message is identified by a selector, which is really an index into a dispatch table.
 * Each method you define and each message you send must have its selector registered.
 * Each selector must be registered with the VM, which will assign a new or existing slot
 * to that selector name.  You keep the slot index and use that when defining methods and
 * messages, instead of the name of the selector.
 *
 */
 
KewSelector kew_selector_register(char *name);

/* CALL SHAPES
 * A call shape records the "structure" of a call:
 * how many arguments there are
 * which of the first 32 arguments are multi args (i.e. the ... syntax)
 * how many results are expected
 * which of the first 32 results is a multi result
 *
 * As with selectors, registering call shapes makes dispatching calls much faster
 * because most calls (90%+) do not require any remarshalling. These calls will have
 * identical call shapes, which leads to a very short code path from the caller to
 * the callee. A longer code path is only invoked if the shapes do not literally
 * match (which implies that at least one of the argument or result shapes do not
 * match).
 */

struct _KewCallShape {
    guint number_of_args;
    guint32 multi_args; /* bit flags */
    guint number_of_results;
    guint32 multi_results; /* bit flags */
};

KewCallShape kew_call_shape_register(guint number_of_args, guint32 multi_args, guint number_of_results, guint32 multi_results);

guint kew_call_shape_number_of_arguments(KewCallShape shape);
guint kew_call_shape_number_of_results(KewCallShape shape);
guint32 kew_call_shape_multi_arguments(KewCallShape shape);
guint32 kew_call_shape_multi_results(KewCallShape shape);

/* This helps to flag each multi-value argument in the send.
 * OR them together and pass in to MULTI_ARG_MASK or MULTI_RESULT_MASK
 */
#define KEW_CALL_SHAPE_MULTI(argno) (1<<(argno))

/* Or you can test access directly from the full call shape using these */
#define KEW_CALL_SHAPE_IS_MULTI_ARG(shape,argno) ((shape) & (1<<(argno)))
#define KEW_CALL_SHAPE_IS_MULTI_RESULT(shape,argno) ((shape) & (1<<((argno)+19)))



struct _KewDispatchMap {
    KewCallShape callee_shape;
    KewMethod method;
    KewSourcePoint source_point;
};

void kew_method_register(KewType *type, KewSelector selector, KewCallShape callee_shape, KewMethod method);

#define kew_method_check(method, object) ((object)->type.dispatch_map[method] != kew_method_not_understood)


/* OBJECT TYPES AND METHODS
 * Each object type must be registered with the VM before objects can be created or
 * methods be attached to the type.
 */

struct _KewObject {
    KewType *type;
    KewSandboxObject *sandbox;
    GHashTable *extensions;
};

struct _KewType {
    /* FIXME: make this an object in its own right to support reflection */

    guint object_size_in_bytes;
    KewSourcePoint source_point;
    GHashTable *method_map;
    GHashTable *extensions;
    KewDispatchMap *dispatch_map;
};

KewType *kew_type_register(KewSourcePoint source_point);

#define kew_type_check(type, object) ((object)->type == (type))




/* METHOD DECLARATIONS
 * By the way, methods get their StaticEnvironment from the receiver, i.e. the context in which
 * the object was created is the context in which it runs.
 */

#define KEW_EXPORT_METHOD(NAME) \
void NAME(KewDynamicEnvironment *dynamic_env, KewObject *receiver, KewObject **args, KewObject **results)

#define KEW_METHOD(NAME) static KEW_EXPORT_METHOD(NAME)

/* This one is used for header files and may one day get some Windows import/export compatibility */
#define KEW_DECLARE_METHOD(NAME) KEW_EXPORT_METHOD(NAME)




/* BASE OBJECT INSTANTIATION
 */

KewObject *kew_object_alloc(KewType *type);

void kew_object_init(KewObject *object);

KewObject *kew_object_new(KewSandboxObject *sandbox, KewType *type);

/* INCOMPLETE OBJECTS
 * (not normally used directly)
 */
 
void kew_incomplete_object_set(KewObject *object);
void kew_incomplete_object_reset(KewUserObject *object, KewType *type);

/* USER-DEFINED OBJECT INSTANTIATION
 */

struct _KewUserObject {
    KewObject super;
    KewObject *capture_slots[0]; /* FIXME: this is probably GCC-specific */
};

void kew_user_object_init(KewUserObject *object);

/* 1-step creation */

KewUserObject *kew_user_object_new(KewType *type);
 
/* 2-step creation: create the reference (an Incomplete object), then fill in the dispatch type */

KewUserObject *kew_user_object_new_begin(KewType *type);

void kew_user_object_new_end(KewUserObject *object, KewType *type);


/* MESSAGE SENDING
 */

void kew_send_full(KewSandboxObject *caller_sandbox,
                   KewDynamicEnvironment *dynamic_env,
                   KewObject *receiver,
                   KewSelector selector,
                   KewCallShape caller_shape,
                   KewObject **args,
                   KewObject **results,
                   KewSourcePoint source_point);



/* The method associated with messages that an object doesn't understand */
KEW_DECLARE_METHOD(kew_not_understood_method);




/* OBJECT EXTENSIONS
 * These allow arbitrary data to be attached to any object instance or type.
 * The key must be a pointer to allocated memory such as an object or a malloced area.
 * Whatever memory is used must remain allocated for the lifetime of the key to prevent
 * the key being accidentally replicated.
 */

void kew_object_add_extension(KewObject *object, gpointer key, gpointer value);
void kew_object_remove_extension(KewObject *object, gpointer key);
gpointer kew_object_get_extension(KewObject *object, gpointer key);

void kew_type_add_extension(KewType *type, gpointer key, gpointer value);
void kew_type_remove_extension(KewType *type, gpointer key);
gpointer kew_type_get_extension(KewType *type, gpointer key);

void kew_thread_add_extension(gpointer key, gpointer value);
void kew_thread_remove_extension(gpointer key);
gpointer kew_thread_get_extension(gpointer key);


/* SOURCE POINTS
 * The Glib macro G_STRLOC provides a useful way to specify the current point in a file
 * in a SourcePoint compatible format (i.e. a string!)
 */

#define KEW_HERE G_STRLOC

G_END_DECLS
#endif
